package com.github.m6d21.conflict.scanner;

import com.google.common.base.Predicate;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.collections.map.UnmodifiableMap;
import org.reflections.scanners.AbstractScanner;
import org.reflections.vfs.Vfs.File;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *<p>
 *.class文件扫面器
 *</p>
 *
 * @author liuyang
 * @date 2018/12/11 Tue 10:32:00
 * @since 1.0.0
 */
public class ClassConflictScanner extends AbstractScanner {

    private static final Logger LOG = LoggerFactory.getLogger(ClassConflictScanner.class);

    private static final String CLASS_SUFFIX = ".class";
    private static final String FILE_SPLIT_REGEXP = "[\\\\/]";
    /**
     * 特殊类名过滤
     */
    private static final String[] SPECIAL_CLASS = new String[]{"module-info.class"};
    /**
     * 包名过滤
     */
    private Predicate<String> filterPackage = null;
    /**
     * 存放历史
     */
    private final static Map<String, String> HISTORY_MAP = new ConcurrentHashMap<>();
    /**
     * 存放冲突Map<类名,Set<路径名>>
     */
    private final static Map<String, Set<String>> CONFLICT_MAP = new ConcurrentHashMap<>();

    /**
     * 过滤class文件，.jar中.class以[文件路径！类文件的相对jar包内部路径]来表示
     */
    @Override
    public boolean acceptsInput(String path) {
        if (path == null) {
            return false;
        }
        String fqn = path.replace('/', '.');
        return path != null && path.endsWith(CLASS_SUFFIX) && isMatchPackages(fqn) && !isSpecialClass(fqn);
    }

    /**
     *refection校验时只要acceptsInput校验成功就会处理，没必要acceptResult重复校验
     */
    @Override
    public boolean acceptResult(String fqn) {
        return false;
    }

    @Override
    public Object scan(File file, Object classObject) {
        //获取类的全名（包名+类名)
        String classFullName = getClassFullName(file.getRelativePath());
        //检测是否出现冲突的类
        String old = HISTORY_MAP.get(classFullName);
        if (old != null) {
            Set<String> set = CONFLICT_MAP.get(classFullName);
            if (set == null) {
                set = new HashSet<>();
            }
            set.add(old);
            //file.toString是文件的绝对路径名（Reflections代码实现这样滴）
            set.add(file.toString());
            CONFLICT_MAP.put(classFullName, set);
        }
        HISTORY_MAP.put(classFullName, file.toString());
        return classObject;
    }

    @Override
    public void scan(Object cls) {
        throw new UnsupportedOperationException();
    }

    /**
     *路径转为类名
     */
    public String getClassFullName(String relativePath) {
        if (relativePath == null || relativePath.trim().length() == 0) {
            return "";
        }
        return relativePath.replaceAll(FILE_SPLIT_REGEXP, ".").replace(CLASS_SUFFIX, "");
    }

    public Map<String, Set<String>> getConflictMap() {
        return UnmodifiableMap.decorate(CONFLICT_MAP);
    }

    /**
     * 过滤包名
     */
    private boolean isMatchPackages(String fqn) {
        if (filterPackage != null) {
            return filterPackage.apply(fqn);
        }
        return true;
    }

    public void clear() {
        clear(CONFLICT_MAP);
        clear(HISTORY_MAP);
    }

    private void clear(Map map) {
        if (map != null) {
            map.clear();
        }
    }

    public Predicate<String> getFilterPackage() {
        return filterPackage;
    }

    public void setFilterPackage(Predicate<String> filterPackage) {
        this.filterPackage = filterPackage;
    }

    private boolean isSpecialClass(String className) {
        for (String s : SPECIAL_CLASS) {
            if (s.equals(className)) {
                return true;
            }
        }
        return false;
    }
}